<script setup>
import { computed, ref, reactive, watch } from "vue";
import "@material/web/button/text-button.js";
import DatePickerHeader from "./DatePickerHeader.vue";
import CalendarItem from "./CalendarItem.vue";
import MonthList from "./MonthList.vue";
import YearList from "./YearList.vue";
import ModalPanel from "@/components/ModalPanel.vue";
import { dateToText } from "../util";

function extractDate(string) {
  const matches = string.match("([0-9]{4})-([0-9]{2})-([0-9]{2})");
  if (matches == null) {
    return null;
  }
  const year = parseInt(matches[1]);
  const month = parseInt(matches[2]) - 1;
  const date = parseInt(matches[3]);
  return new Date(year, month, date);
}

const model = defineModel();
const emit = defineEmits(["submit", "cancel"]);
const props = defineProps(["open"]);

const state = ref("calendar");
const today = new Date();
const selected = ref(model.value || dateToText(today));
const selectedDate = computed(() => extractDate(selected.value));
const showing = reactive({
  year: (selectedDate.value || new Date()).getFullYear(),
  month: (selectedDate.value || new Date())?.getMonth()
});
const calendarTransition = ref("");
function updateShowing({ year, month }) {
  showing.year = year;
  showing.month = month;
}
watch(model, model => {
  selected.value = model;
});
watch(selectedDate, selectedDate => {
  updateShowing({
    year: (selectedDate || new Date()).getFullYear(),
    month: (selectedDate || new Date()).getMonth()
  });
});
watch(() => ({ month: showing.month, year: showing.year }), (newShowing, oldShowing) => {
  let oldValue = oldShowing.year * 12 + (oldShowing.month + 1);
  let newValue = newShowing.year * 12 + (newShowing.month + 1);
  if (newValue > oldValue) {
    calendarTransition.value = "calendar-next";
  } else {
    calendarTransition.value = "calendar-prev";
  }
});

function select(date) {
  selected.value = dateToText(date);
}

function trigger(target) {
  if (state.value == target) {
    state.value = "calendar";
  } else {
    state.value = target;
  }
}

function monthChange(month) {
  showing.month = month;
  state.value = "calendar";
}

function yearChange(year) {
  showing.year = year;
  state.value = "calendar";
}

function submit() {
  model.value = selected.value;
  emit("submit");
}

function cancel() {
  selected.value = model.value || dateToText(today);
  emit("cancel");
}

const calendarContainer = ref(null);
const calendarHeight = ref(null);
function calcCalendarHeight() {
  function calcContentHeight(item) {
    return Array.prototype.map.call(item.children, item => {
      return item.offsetHeight;
    }).reduce((total, num) => total + num, 0);
  }
  let date = `${showing.year}-${showing.month}`;
  let current = Array.prototype.filter.call(calendarContainer.value.children, item => item.dataset.date == date)[0];
  let height = calcContentHeight(current);
  calendarHeight.value = height + "px";
}
watch(() => {
  return props.open;
}, open => {
  if (open) {
    setTimeout(() => {
      calcCalendarHeight();
    });
  }
});
</script>

<template>
  <ModalPanel class="date-picker-panel" :open="props.open">
    <template #content>
      <DatePickerHeader class="date-picker-header" :month="showing.month" :year="showing.year" :state="state"
        @change="updateShowing" @monthClick="trigger('month')" @yearClick="trigger('year')">
      </DatePickerHeader>
      <div class="date-picker-content" :style="{ height: calendarHeight }">
        <div ref="calendarContainer" role="presentation" class="calendar-container">
          <Transition :name="calendarTransition" @enter="calcCalendarHeight">
            <CalendarItem :key="`${showing.year}-${showing.month}`" :data-date="`${showing.year}-${showing.month}`"
              class="calendar" :month="showing.month" :year="showing.year" :selectedDate="selectedDate"
              @change="select">
            </CalendarItem>
          </Transition>
        </div>
        <Transition name="overlay">
          <MonthList class="overlay" :month="showing.month" v-if="state == 'month'" @change="monthChange"></MonthList>
        </Transition>
        <Transition name="overlay">
          <YearList class="overlay" :year="showing.year" v-if="state == 'year'" @change="yearChange"></YearList>
        </Transition>
      </div>
    </template>
    <template #actions>
      <div class="date-picker-footer">
        <md-text-button @click="submit">确定</md-text-button>
        <md-text-button @click="cancel">取消</md-text-button>
      </div>
    </template>
  </ModalPanel>
</template>

<style scoped>
.date-picker-panel {
  --modal-panel-shape: 12px;
  --modal-panel-background: var(--md-sys-color-surface-container-high);
  position: absolute;
  z-index: 1;
}

.date-picker-header {
  margin-bottom: 12px;
  margin-top: 8px;
  margin-left: 16px;
  margin-right: 16px;
}

.date-picker-footer {
  margin-top: 16px;
  display: flex;
  flex-direction: row-reverse;
  gap: 8px;
  margin-left: 16px;
  margin-right: 16px;
  margin-bottom: 8px;
}

.date-picker-content {
  position: relative;
  overflow: hidden;
  transition-property: height;
  transition-duration: var(--md-sys-motion-duration-short4);
  transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate);
}

.overlay {
  background-color: var(--md-sys-color-surface-container-high);
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 0;
}

.overlay-enter-active,
.overlay-leave-active {
  transition-property: opacity;
  transition-duration: var(--md-sys-motion-duration-medium2);
  transition-timing-function: var(--md-sys-motion-easing-standard);
}

.overlay-enter-to,
.overlay-leave-from {
  opacity: 1;
}

.overlay-enter-from,
.overlay-leave-to {
  opacity: 0;
}

.calendar-container {
  display: grid;
  grid-template-areas: "slot";
  overflow: hidden;
}

.calendar {
  margin-left: 16px;
  margin-right: 16px;
  grid-area: slot;
}

.calendar-next-enter-active,
.calendar-next-leave-active,
.calendar-prev-enter-active,
.calendar-prev-leave-active {
  transition-property: transform;
  transition-duration: var(--md-sys-motion-duration-short4);
  transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate);
}

.calendar-next-enter-to,
.calendar-next-leave-from,
.calendar-prev-enter-to,
.calendar-prev-leave-from {
  transform: translateX(0%);
}

.calendar-next-enter-from,
.calendar-prev-leave-to {
  transform: translateX(100%);
}

.calendar-next-leave-to,
.calendar-prev-enter-from {
  transform: translateX(-100%);
}
</style>